# --*--utf8--*--
from ctypes import *
import os
import sys

from ..utils.loggers import dev_logger, prod_logger


class UsbcanConstant:
    # if 1:
    # with pysnooper.snoop():

    @staticmethod
    def getTiming(mbaud):
        if mbaud == "1M":
            return 0, 0x14
        if mbaud == "800k":
            return 0, 0x16
        if mbaud == "666k":
            return 0x80, 0xB6
        if mbaud == "500k":
            return 0, 0x1C
        if mbaud == "400k":
            return 0x80, 0xFA
        if mbaud == "250k":
            return 0x01, 0x1C
        if mbaud == "200k":
            return 0x81, 0xFA
        if mbaud == "125k":
            return 0x03, 0x1C
        if mbaud == "100k":
            return 0x04, 0x1C
        if mbaud == "80k":
            return 0x83, 0xFF
        if mbaud == "50k":
            return 0x09, 0x1C

    """
    全局变量
    """
    DevType = c_uint

    """
        Device Type
    """
    USBCAN1 = DevType(3)
    USBCAN2 = DevType(4)
    USBCANFD = DevType(6)
    """
        Device Index
    """
    DevIndex = c_uint(0)  # 设备索引
    """
        Channel
    """
    Channel1 = c_uint(0)  # CAN1
    Channel2 = c_uint(1)  # CAN2
    """
        ECAN Status
    """
    STATUS_ERR = 0
    STATUS_OK = 1

    """
        Device Information
    """


# if 1:

"""
can相关数据结构
"""


class BoardInfo(Structure):
    _fields_ = [
        ("hw_Version", c_ushort),  # 硬件版本号，用16进制表示
        ("fw_Version", c_ushort),  # 固件版本号，用16进制表示
        ("dr_Version", c_ushort),  # 驱动程序版本号，用16进制表示
        ("in_Version", c_ushort),  # 接口库版本号，用16进制表示
        ("irq_Num", c_ushort),  # 板卡所使用的中断号
        ("can_Num", c_byte),  # 表示有几路CAN通道
        ("str_Serial_Num", c_byte * 20),  # 此板卡的序列号，用ASC码表示
        ("str_hw_Type", c_byte * 40),  # 硬件类型，用ASC码表示
        ("Reserved", c_byte * 4),
    ]  # 系统保留


class CAN_OBJ(Structure):
    _fields_ = [
        ("ID", c_uint),  # 报文帧ID
        ("TimeStamp", c_uint),  # 接收到信息帧时的时间标识，从CAN控制器初始化开始计时，单位微秒
        (
            "TimeFlag",
            c_byte,
        ),  # 是否使用时间标识，为1时TimeStamp有效，TimeFlag和TimeStamp只在此帧为接收帧时有意义。
        ("SendType", c_byte),
        # 发送帧类型。=0时为正常发送，=1时为单次发送（不自动重发），=2时为自发自收（用于测试CAN卡是否损坏），=3时为单次自发自收（只发送一次，用于自测试），只在此帧为发送帧时有意义
        ("RemoteFlag", c_byte),  # 是否是远程帧。=0时为数据帧，=1时为远程帧
        ("ExternFlag", c_byte),  # 是否是扩展帧。=0时为标准帧（11位帧ID），=1时为扩展帧（29位帧ID）
        ("DataLen", c_byte),  # 数据长度DLC(<=8)，即Data的长度
        ("data", c_ubyte * 8),  # CAN报文的数据。空间受DataLen的约束
        ("Reserved", c_byte * 3),
    ]  # 系统保留。


class INIT_CONFIG(Structure):
    _fields_ = [
        ("acccode", c_uint32),  # 验收码。SJA1000的帧过滤验收码
        ("accmask", c_uint32),  # 屏蔽码。SJA1000的帧过滤屏蔽码。屏蔽码推荐设置为0xFFFF FFFF，即全部接收
        ("reserved", c_uint32),  # 保留
        ("filter", c_byte),  # 滤波使能。0=不使能，1=使能。使能时，请参照SJA1000验收滤波器设置验收码和屏蔽码
        ("timing0", c_byte),  # 波特率定时器0,详见动态库使用说明书7页
        ("timing1", c_byte),  # 波特率定时器1,详见动态库使用说明书7页
        ("mode", c_byte),
    ]  # 模式。=0为正常模式，=1为只听模式，=2为自发自收模式。


class ECAN(object):
    def __init__(self):
        cwdx = os.path.join(
            os.path.dirname(os.path.abspath((__file__))), "eCanVci64.dll"
        )
        self.dll = cdll.LoadLibrary(cwdx)
        if self.dll == None:
            prod_logger.error("DLL Couldn't be loaded")

    def OpenDevice(self, DeviceType, DeviceIndex):
        try:
            return self.dll.OpenDevice(DeviceType, DeviceIndex, 0)
        except:
            prod_logger.error("Exception on OpenDevice!")
            raise

    def CloseDevice(self, DeviceType, DeviceIndex):
        try:
            return self.dll.CloseDevice(DeviceType, DeviceIndex, 0)
        except:
            prod_logger.error("Exception on CloseDevice!")
            raise

    def InitCan(self, DeviceType, DeviceIndex, CanInd, Initconfig):
        try:
            return self.dll.InitCAN(
                DeviceType, DeviceIndex, CanInd, byref(Initconfig)
            )
        except:
            prod_logger.error("Exception on InitCan!")
            raise

    def StartCan(self, DeviceType, DeviceIndex, CanInd):
        try:
            return self.dll.StartCAN(DeviceType, DeviceIndex, CanInd)
        except:
            prod_logger.error("Exception on StartCan!")
            raise

    def ReadBoardInfo(self, DeviceType, DeviceIndex):
        try:
            mboardinfo = BoardInfo()
            ret = self.dll.ReadBoardInfo(
                DeviceType, DeviceIndex, byref(mboardinfo))
            return mboardinfo, ret
        except:
            prod_logger.error("Exception on ReadBoardInfo!")
            raise

    def Receivce(self, DeviceType, DeviceIndex, CanInd, length):
        try:
            recmess = (CAN_OBJ * length)()
            ret = self.dll.Receive(
                DeviceType, DeviceIndex, CanInd, byref(recmess), length, 0
            )
            return length, recmess, ret
        except:
            prod_logger.error("Exception on Receive!")
            raise

    def Tramsmit(self, DeviceType, DeviceIndex, CanInd, mcanobj):
        try:
            # mCAN_OBJ=CAN_OBJ*2
            # self.dll.Transmit.argtypes = [ctypes.c_uint32, ctypes.c_uint32, ctypes.c_uint32, POINTER(CAN_OBJ),
            # ctypes.c_uint16]
            return self.dll.Transmit(
                DeviceType, DeviceIndex, CanInd, byref(mcanobj), c_uint16(1)
            )
        except:
            prod_logger.error("Exception on Tramsmit!")
            raise

    # 获取指定接收缓存区中接收到，但尚未被读取的帧数量
    def GetReceiveNum(self, DeviceType, DeviceIndex, CanInd):
        try:
            # 返回尚未被读取的帧数
            bufferCounts = self.dll.GetReceiveNum(
                DeviceType, DeviceIndex, CanInd)
            # dev_logger.info(f'当前Buffer中缓存的帧数为{bufferCounts}条')
            return bufferCounts
        except:
            prod_logger.error('Exception on GetReceiveNum !')
            raise

    # 增加clearBuffer功能
    def ClearBuffer(self, DeviceType, DeviceIndex, CanInd):

        # 先查看缓冲区有多少条数据？
        self.GetReceiveNum(DeviceType, DeviceIndex, CanInd)

        try:
            # 返回1表示操作成功，返回0表示操作失败
            ret = self.dll.ClearBuffer(DeviceType, DeviceIndex, CanInd)
            self.GetReceiveNum(DeviceType, DeviceIndex, CanInd)
            return ret
        except:
            prod_logger.error('Exception on ClearBuffer!')
            raise
